using System;
using System.Data;
using System.Text;
using System.Collections;

using BizElements.Core.ProviderComponents;

namespace BizElements.Core
{
	#region Documentation.
	/// <summary>
	/// Changes existing data in a table.
	/// </summary>
	/// <example>
	/// The following example updates the phone number of the employee with the specified primary key (EmployeeID) value.
	/// <code>
	/// public void UpdateEmployeesPhoneNumber(int employeeId, string newPhone)
	/// {
	/// 	//	UPDATE	Employees 
	/// 	//	SET		HomePhone = @newPhone
	/// 	//	WHERE	Employees.EmployeeID = @employeeId
	/// 
	/// 	EmployeesMeta employees = new EmployeesMeta();
	/// 	UpdateStatement update = new UpdateStatement(employees);
	/// 	update.UpdateList.Add(employees.HomePhone, newPhone);
	/// 	update.Where.Add(PredicateFactory.Compare(employees.EmployeeID, "=", employeeId));
	/// 
	/// 	update.Execute();
	/// }
	/// </code>
	/// </example>
	#endregion
	[Serializable]
	public sealed class UpdateStatement : SqlStatementBase
	{
		#region Members.
		
		UpdateList setList = new UpdateList();
		SearchCondition where = new SearchCondition();
        RelationBucket relations = new RelationBucket();

		#endregion

		#region Constructors.

		/// <summary>
		/// Creates a new UPDATE statement that updates rows in the given table.
		/// </summary>
		/// <param name="table">The table to update.</param>
		public UpdateStatement(IDbTable table) 
			: base(table)
		{ 
		}

		/// <summary>
		/// Creates a new UPDATE statement that updates rows in the given table.
		/// </summary>
		/// <param name="table">The table to update.</param>
		/// <param name="columnToUpdate">First columns in the <see cref="UpdateList"/>. Additional items may be specified in <see cref="UpdateList"/> collection.</param>
		/// <param name="newValue">New value of the specified field. May be <b>null</b>.</param>
		public UpdateStatement(IDbTable table, IDbColumn columnToUpdate, object newValue)
			: this(table)
		{
			this.UpdateList.Add(columnToUpdate, newValue);
		}

		#endregion

		#region Methods.

		/// <summary>
		/// Renders UPDATE statement.
		/// </summary>
		/// <param name="dbms">Target DBMS.</param>
		/// <param name="output">StringBuilder to which SQL is appended.</param>
		/// <param name="parameters">SQL parameter collection to which the object's and its children's
		/// parameters are added. After the rendering is done the collection contains all parameters with unique names.</param>
		public override void Render(DbmsType dbms, StringBuilder output, DbParameterCollection parameters)
		{
            IUpdater renderer = DbmsComponentFactory.GetComponent<IUpdater>(dbms);
            renderer.RenderUpdate(this, dbms, output, parameters);
        }

		/// <summary>
		/// Executes the UPDATE command.
		/// </summary>
		/// <returns>The number of rows affected.</returns>
		public int Execute()
		{
            IUpdater executor = DbmsComponentFactory.GetComponent<IUpdater>(this.ConnectionProvider.DBMS);
            CommandExecutionStatistics lastExecutedCommandInfo;
            int rowsAffected = executor.Execute(this, this.ConnectionProvider.DBMS, this.ConnectionProvider, out lastExecutedCommandInfo);
            this.LastExecutedCommandInfo = lastExecutedCommandInfo;
            return rowsAffected;
		}

		/// <summary>
		/// Executes the UPDATE command. Uses the given connection provider.
		/// </summary>
		/// <param name="conn">Connection provider.</param>
		/// <returns>The number of rows affected.</returns>
		public int Execute(IConnectionProvider conn)
		{
			this.ConnectionProvider = conn;
			return Execute();
		}
	
		#endregion

		#region Properties.

		/// <summary>
		/// Gets the table to update.
		/// </summary>
		public IDbTable Table
		{
            get { return TargetTable; }		
		}

		/// <summary>
		/// Gets or sets the list of columns to be updated.
		/// </summary>
		public UpdateList UpdateList
		{
			get {return this.setList;}
			set {this.setList = value;}
		}

		/// <summary>
		/// Gets or sets the search condition which limits the number of rows that are updated.
		/// </summary>
		public SearchCondition Where
		{
			get {return this.where;}
			set {this.where = value;}
		}

        /// <summary>
        /// Gets or sets the <see cref="RelationBucket"/> that contains the relations used by this statement.
        /// Required when the SelectStatement retrieves columns from multiple tables.
        /// </summary>
        public RelationBucket Relations
        {
            get { return this.relations; }
        }

		#endregion
	}			
}
